﻿using System;
using System.Linq;
using Notoric.Commanding.CommandBase;

namespace Notoric.ViewModel
{
    /// <summary>
    /// An undoable command for creating a new part descriptor.
    /// </summary>
    /// <remarks>
    /// Input parameters:
    /// <list type="table">
    /// <listheader>
    /// <term>
    ///     Name
    /// </term>
    /// <description>
    ///     Description
    /// </description>
    /// </listheader>
    /// <item>
    /// <term>
    ///     <see cref="Abbreviation"/> ("<see cref="string"/>)
    /// </term>
    /// <description>
    ///     The abbreviation of the part's name.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="Composition"/> (<see cref="Elements.Composition"/>)
    /// </term>
    /// <description>
    ///     The composition in which the new part will be created.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="Index"/> (<see cref="Int32"/>)
    /// </term>
    /// <description>
    ///     The index in the parts collection where the new part will be inserted.
    ///     The default value is -1 meaning that the part will be added to the end
    ///     of the collection.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="PartName"/> (<see cref="string"/>)
    /// </term>
    /// <description>
    ///     The name of the part.
    /// </description>
    /// </item>
    /// </list>
    /// Output parameters:
    /// <list type="table">
    /// <listheader>
    /// <term>
    ///     Name
    /// </term>
    /// <description>
    ///     Description
    /// </description>
    /// </listheader>
    /// <item>
    /// <term>
    ///     <see cref="CreatedPart"/> (<see cref="Elements.PartDescriptor"/>)
    /// </term>
    /// <description>
    ///     The created part descriptor.
    /// </description>
    /// </item>
    /// </list>
    /// </remarks>
    public class NewPartDescriptorCommand : UndoableCommandBase
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="NewPartDescriptorCommand"/> class.
        /// </summary>
        /// <param name="undoStack">The undo command stack</param>
        /// <param name="redoStack">The redo command stack</param>
        public NewPartDescriptorCommand(CommandStack undoStack, CommandStack redoStack)
            : base(undoStack, redoStack)
        {
            AddParameter<string>("Abbreviation", ParameterDirection.In);
            AddParameter<Elements.Composition>("Composition", ParameterDirection.In);
            AddParameter<int>("Index", ParameterDirection.In);
            AddParameter<string>("PartName", ParameterDirection.In);

            AddParameter<Elements.PartDescriptor>("CreatedPart", ParameterDirection.Out);

            Name = "Create New Part";
        }

        #endregion

        #region Command Logic

        /// <inheritdoc/>
        protected override void CommandOperation(object parameter)
        {
            var modelComposition = Composition.AdaptedElement as Model.Elements.CompositionElement;
            if (Index == -1)
                Index = modelComposition.Parts.Count;

            var part = modelComposition.CreatePartDescriptor(PartName);
            modelComposition.Parts.Insert(Index, part);
            CreatedPart = Composition.Parts.First(p => p.AdaptedElement == part);

            Composition.FindOwner<Elements.Project>().IsModified = true;
        }

        /// <inheritdoc/>
        protected override void RedoOperation()
        {
            var modelComposition = Composition.AdaptedElement as Model.Elements.CompositionElement;
            modelComposition.Parts.Insert(Index, CreatedPart.AdaptedElement as Model.Elements.PartDescriptorElement);
            var vmPart = Composition.Parts.First(p => p.AdaptedElement == CreatedPart.AdaptedElement);
            Composition.Parts[Composition.Parts.IndexOf(vmPart)] = CreatedPart;

            Composition.FindOwner<Elements.Project>().IsModified = true;
        }

        /// <inheritdoc/>
        protected override void UndoOperation()
        {
            var modelComposition = Composition.AdaptedElement as Model.Elements.CompositionElement;
            modelComposition.Parts.RemoveAt(Index);

            Composition.FindOwner<Elements.Project>().IsModified = true;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the abbreviation of the part name.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// The abbreviation of the part name.<br/>
        /// This property wraps the "Abbreviation" command parameter.
        /// </para>
        /// </value>
        public string Abbreviation
        {
            get { return GetParameterValue<string>("Abbreviation"); }
            set { SetParameterValue<string>("Abbreviation", value); }
        }

        /// <summary>
        /// Gets or sets the composition in which the part will be created.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Elements.Composition"/>
        /// </para>
        /// <para>
        /// The composition in which the part will be created.<br/>
        /// This property wraps the "Composition" command parameter.
        /// </para>
        /// </value>
        public Elements.Composition Composition
        {
            get { return GetParameterValue<Elements.Composition>("Composition"); }
            set { SetParameterValue<Elements.Composition>("Composition", value); }
        }

        /// <summary>
        /// Gets the created part.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Elements.PartDescriptor"/>
        /// </para>
        /// <para>
        /// The part created by this command.<br/>
        /// This property wraps the "CreatedPart" command parameter.
        /// </para>
        /// </value>
        public Elements.PartDescriptor CreatedPart
        {
            get { return GetParameterValue<Elements.PartDescriptor>("CreatedPart"); }
            private set { SetParameterValue<Elements.PartDescriptor>("CreatedPart", value); }
        }

        /// <summary>
        /// Gets or sets the index of the part.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Int32"/>
        /// </para>
        /// <para>
        /// The index (0-based) of the created part in the parts collection
        /// of the <see cref="Composition"/>. Default value is -1 meaning
        /// that the part will be added to the end of the collection.<br/>
        /// This property wraps the "Index" command parameter.
        /// </para>
        /// </value>
        public int Index
        {
            get { return GetParameterValue<int>("Index"); }
            set { SetParameterValue<int>("Index", value); }
        }

        /// <summary>
        /// Gets or sets the name of the part.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// The name of the created part.<br/>
        /// This property wraps the "PartName" command parameter.
        /// </para>
        /// </value>
        public string PartName
        {
            get { return GetParameterValue<string>("PartName"); }
            set { SetParameterValue<string>("PartName", value); }
        }

        #endregion
    }
}
